package com.yanxin.workflow.date;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashSet;

public class DateUtils {

    /**
     * SLA 时间计算，按工作时间09:00~18：00,包括周末,节假日.
     *
     * @param dateStr      提单时间
     * @param offsetMinute 处理时间/ 按分钟算
     * @return SLA的处理时间
     */
    public static long calculateDate(String dateStr, int offsetMinute) {

        // now为开始计算时间,判断是当天还是明天,18:00后统一算明天
        LocalDateTime now = initDate(dateStr);

        LocalDateTime workEndTime = now.withHour(18).withMinute(0)
                .withSecond(0).withNano(0);

        // 初始化第一个工作日
        now = loop(now, 1);
        workEndTime = loop(workEndTime, 1);

        // 1天的分钟数
        long offsetDayMinutes = 540L;

        // 偏移时间小于1天
        if (offsetMinute / offsetDayMinutes <= 1) {
            LocalDateTime plusTime = LocalDateTimeUtil.offset(now, offsetMinute, DateUnit.MINUTE.toChronoUnit());
            // 偏移后时间是在18点前
            if (plusTime.isBefore(workEndTime) || plusTime.isEqual(workEndTime)) {
                LocalDateTime deadline = now
                        .plusMinutes(offsetMinute);
                System.out.println("SLA less than 1 day time: %s" + deadline);
            } else {
                // 偏移后时间在18点后,返回累加一天后的第一个工作日 计算时间差
                Duration betweenTime = Duration.between(now, workEndTime);
                long minutes = betweenTime.toMinutes();
                long finalPlusTime = offsetMinute - minutes;
                // off set 1 day
                now = LocalDateTimeUtil.offset(now, 1, DateUnit.DAY.toChronoUnit());
                now = loop(now, 1);
                LocalDateTime minusTime = LocalDateTimeUtil.offset(now, finalPlusTime, DateUnit.MINUTE.toChronoUnit());
                System.out.println("SLA more than 1 day time: %s" + minusTime);
            }
            return 1L;
        }


        // 如果时间大于1天,不能大于0，因为已经偏移到第一个工作日了.
        while (offsetMinute / offsetDayMinutes > 1) {
            now = LocalDateTimeUtil.offset(now, 1, DateUnit.DAY.toChronoUnit());
            offsetMinute -= offsetDayMinutes;
            now = loop(now, 1);
        }
        now = loop(now, 1);
        // 重新赋值工作结束时间
        workEndTime = now.withHour(18).withMinute(0)
                .withSecond(0).withNano(0);

        // 剩于时间
        long plusMinute = Math.abs(offsetMinute);

        if (plusMinute == 0) {
            // 往前推到18:00. 如果offset -1 day,也得判断这天是否是休息日或周末，需要往前倒推到第一个工作日
            now = LocalDateTimeUtil.offset(now, -1, DateUnit.DAY.toChronoUnit())
                    .withHour(18).withMinute(0).withSecond(0).withNano(0);
            // 往前倒推工作日
            now = loop(now, -1);
            System.out.println("due time: %s" + now);
            return 1L;
        }

        LocalDateTime deadline = now.plusMinutes(plusMinute);
        if (deadline.isBefore(workEndTime) || deadline.isEqual(workEndTime)) {
            LocalDateTime deadlinePlus = now.plusMinutes(offsetMinute);
            System.out.println("SLA while less  time: %s" + deadlinePlus);
        } else {
            // 偏移后时间在18点后,返回累加一天后的第一个工作日 计算时间差
            Duration betweenTime = Duration.between(now, deadline);
            long minutes = betweenTime.toMinutes();
            long finalPlusTime = offsetMinute - minutes;
            // off set 1 day
            now = LocalDateTimeUtil.offset(now, 1, DateUnit.DAY.toChronoUnit());
            now = loop(now, 1);
            LocalDateTime minusTime = now.plusMinutes(finalPlusTime);
            System.out.println("SLA while more  time: %s" + minusTime);
        }
        return 1L;
    }

    /**
     * 递归 推进或者倒推工作日
     *
     * @param future 日期
     * @param number 1 or -1, 1 是往后推，-1 是往前推
     * @return 最后的工作日
     */
    public static LocalDateTime loop(LocalDateTime future, int number) {
        if (LocalDateTimeUtil.isWeekend(future)) { // 9 15
            if (isWorkDay(future)) {
                return future;
            } else {
                future = LocalDateTimeUtil.offset(future, number, DateUnit.DAY.toChronoUnit());
                return loop(future, number);
            }
        } else {
            if (isHoliday(future)) {
                future = LocalDateTimeUtil.offset(future, number, DateUnit.DAY.toChronoUnit());
                return loop(future, number);
            } else {
                return future;
            }
        }
    }

    public static boolean isHoliday(LocalDateTime date) {

        // https://date.nager.at/api/v3/PublicHolidays/2024/CN 节假日API
        HashSet<LocalDateTime> holidays = new HashSet<>();
        holidays.add(LocalDateTimeUtil.parse("2024-01-01", "yyyy-MM-dd"));
        holidays.add(LocalDateTimeUtil.parse("2024-02-10", "yyyy-MM-dd"));
        holidays.add(LocalDateTimeUtil.parse("2024-04-05", "yyyy-MM-dd"));
        holidays.add(LocalDateTimeUtil.parse("2024-05-01", "yyyy-MM-dd"));
        holidays.add(LocalDateTimeUtil.parse("2024-06-10", "yyyy-MM-dd"));
        holidays.add(LocalDateTimeUtil.parse("2024-09-16", "yyyy-MM-dd"));
        holidays.add(LocalDateTimeUtil.parse("2024-09-17", "yyyy-MM-dd"));
        holidays.add(LocalDateTimeUtil.parse("2024-10-01", "yyyy-MM-dd"));
        for (LocalDateTime workday : holidays) {
            if (LocalDateTimeUtil.isSameDay(workday, date)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isWorkDay(LocalDateTime date) {
        // 补班日期
        HashSet<LocalDateTime> workdays = new HashSet<>();
        workdays.add(LocalDateTimeUtil.parse("2024-02-04", "yyyy-MM-dd"));
        workdays.add(LocalDateTimeUtil.parse("2024-02-18", "yyyy-MM-dd"));
        workdays.add(LocalDateTimeUtil.parse("2024-04-07", "yyyy-MM-dd"));
        workdays.add(LocalDateTimeUtil.parse("2024-04-28", "yyyy-MM-dd"));
        workdays.add(LocalDateTimeUtil.parse("2024-05-11", "yyyy-MM-dd"));
        workdays.add(LocalDateTimeUtil.parse("2024-09-14", "yyyy-MM-dd"));
        workdays.add(LocalDateTimeUtil.parse("2024-09-29", "yyyy-MM-dd"));
        workdays.add(LocalDateTimeUtil.parse("2024-10-12", "yyyy-MM-dd"));
        for (LocalDateTime workday : workdays) {
            if (LocalDateTimeUtil.isSameDay(workday, date)) {
                return true;
            }
        }
        return false;
    }

    public static LocalDateTime initDate(String dateStr) {

        LocalDateTime now = DateUtil.parseLocalDateTime(dateStr, "yyyy-MM-dd HH:mm:ss");
        // 当天的工作时间 9:00 - 18:00
        LocalDateTime workStartTime = now.withHour(9).withMinute(0)
                .withSecond(0).withNano(0);

        LocalDateTime workEndTime = workStartTime.withHour(18).withMinute(0)
                .withSecond(0).withNano(0);

        // 9点前, 则从当天的9:00开始加分钟数
        if (now.isBefore(workStartTime)) {
            now = workStartTime;
        }

        // 18点后, 则从第二天的9:00开始加分钟数
        if (now.isAfter(workEndTime) || now.isEqual(workEndTime)) {
            now = LocalDateTimeUtil.offset(now, 1, DateUnit.DAY.toChronoUnit())
                    .withHour(9).withMinute(0).withSecond(0).withNano(0);
        }
        return now;
    }


    public static void main(String[] args) {
        // pass  2024-09-29T10:00
        // calculateDate("2024-09-27 08:50:00", 600);
        // pass 2024-09-14T10:00
        // calculateDate("2024-09-13 08:50:00", 600);

        // pass 2024-09-14T09:20
        // calculateDate("2024-09-13 18:50:00", 20);
        // due time: 2024-09-14T11:40
        // calculateDate("2024-09-13 02:50:00", 700);

        // pass 2024-09-18T09:20
        // calculateDate("2024-09-14 18:50:00", 20);

        // pass 2024-09-14T09:20
        // calculateDate("2024-09-14 02:50:00", 20);

        // 20天 %s2024-09-29T18:00
        calculateDate("2024-09-01 10:00:00", 10800);
    }
}
